export function chunkArray(arr: any[] = [], chunkSize = 200) {
  const result: any[] = [];
  const len = arr.length;

  // 循环分块
  for (let i = 0; i < len; i += chunkSize) {
    result.push(arr.slice(i, i + chunkSize));
  }

  return result;
}

export function sendMessage<T>(message: any): Promise<T> {
  return new Promise((resolve, reject) => {
    try {
      chrome.runtime.sendMessage(message, (response) => {
        // 检查是否有错误
        if (chrome.runtime.lastError) {
          // 忽略 "The message port closed before a response was received" 错误
          // 这通常发生在接收端没有调用 sendResponse 或连接已关闭时
          if (
            chrome.runtime.lastError.message?.includes(
              "The message port closed before a response was received"
            )
          ) {
            // 对于某些不需要响应的消息，静默忽略此错误
            resolve(undefined as T);
            return;
          }
          // 其他错误则 reject
          reject(new Error(chrome.runtime.lastError.message));
          return;
        }
        resolve(response);
      });
    } catch (error) {
      reject(error instanceof Error ? error : new Error(String(error)));
    }
  });
}

// 检查token是否过期（针对时间戳格式）
export function checkTokenExpired(expiresIn: string | number): boolean {
  if (!expiresIn) return true;

  // 统一转换为数字
  const expiryTime =
    typeof expiresIn === "string" ? parseInt(expiresIn) : expiresIn;

  // 获取当前时间戳（毫秒）
  const currentTime = new Date().getTime();
  return currentTime > expiryTime;
}

// 压缩text -> Uint8Array
export async function compress(text: string) {
  const sanitized = normalizeTextForCompression(text);
  const encoder = new TextEncoder();
  const data = encoder.encode(sanitized); // 原文转二进制
  const cs = new CompressionStream("gzip");
  const writer = cs.writable.getWriter();
  writer.write(data);
  writer.close();

  // 从流中读取压缩后的二进制数据（Uint8Array 类型的分块）
  const reader = cs.readable.getReader();
  const chunks = [];
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value); // value 是 Uint8Array 类型
  }

  // 合并分块为完整的 Uint8Array
  const compressed = new Uint8Array(
    chunks.reduce((total, chunk) => total + chunk.length, 0)
  );
  let offset = 0;
  chunks.forEach((chunk) => {
    compressed.set(chunk, offset);
    offset += chunk.length;
  });

  // 打印压缩后大小, 单位 KB（保留两位小数）
  // const sizeKB = (compressed.length / 1024).toFixed(2);
  // console.log(`压缩后大小: ${sizeKB} KB`);

  return compressed;
}

// 规范化文本，移除零宽空白、双向控制字符及常见对应实体，避免压缩/解压后显示异常
export function normalizeTextForCompression(input: string) {
  if (!input) return input;

  let text = input;

  // 先移除常见的 HTML 实体及其数字/十六进制形式
  text = text
    .replace(/&lrm;/gi, " ") // LEFT-TO-RIGHT MARK
    .replace(/&rlm;/gi, " ") // RIGHT-TO-LEFT MARK
    .replace(/&#8206;|&#x200E;/gi, "") // LRM 数字/十六进制实体
    .replace(/&#8207;|&#x200F;/gi, ""); // RLM 数字/十六进制实体

  // 移除不可见与双向控制字符：
  // \u200B ZERO WIDTH SPACE
  // \u200C ZWNJ, \u200D ZWJ, \u200E LRM, \u200F RLM
  // \u202A..\u202E 双向嵌入/覆盖，\u2066..\u2069 双向隔离
  // \uFEFF BOM
  const INVISIBLE_AND_BIDI = /[\u200B-\u200F\u202A-\u202E\u2066-\u2069\uFEFF]/g;
  text = text.replace(INVISIBLE_AND_BIDI, "");

  // 解码常见 HTML 实体（命名与数字，含十六进制），例如
  // &lt; &gt; &amp; &quot; &apos; &lbrack; &rbrack; 以及 &#60; &#x3C; 等
  text = decodeHtmlEntities(text);

  return text;
}

// 解码 HTML 实体到真实字符（支持命名实体与十进制/十六进制数字实体）
function decodeHtmlEntities(source: string): string {
  if (!source) return source;

  const namedMap: Record<string, string> = {
    lt: "<",
    gt: ">",
    amp: "&",
    quot: '"',
    apos: "'",
    lbrack: "[",
    rbrack: "]",
  };

  return source.replace(/&(#x[0-9a-fA-F]+|#\d+|[a-zA-Z]+);/g, (m, p1) => {
    if (!p1) return m;
    // 命名实体
    const lower = String(p1).toLowerCase();
    if (namedMap[lower] !== undefined) {
      return namedMap[lower];
    }

    // 数字实体（十六进制或十进制）
    if (lower.startsWith("#x")) {
      const codePoint = parseInt(lower.slice(2), 16);
      if (Number.isFinite(codePoint)) {
        try {
          return String.fromCodePoint(codePoint);
        } catch {
          return m;
        }
      }
      return m;
    }
    if (lower.startsWith("#")) {
      const codePoint = parseInt(lower.slice(1), 10);
      if (Number.isFinite(codePoint)) {
        try {
          return String.fromCodePoint(codePoint);
        } catch {
          return m;
        }
      }
      return m;
    }

    // 未知命名实体，保留原样
    return m;
  });
}

export function getSiteCountryByUrl(url: string) {
  if (url.includes(".amazon.co.uk")) {
    return "GBR";
  } else if (url.includes(".amazon.de")) {
    return "DEU";
  } else if (url.includes(".amazon.co.jp")) {
    return "JPN";
  } else if (url.includes(".amazon.com.mx")) {
    return "MEX";
  } else if (url.includes(".amazon.ca")) {
    return "CAN";
  } else if (url.includes(".amazon.com.au")) {
    return "AUS";
  } else if (url.includes(".amazon.sa")) {
    return "SAU";
  } else if (url.includes(".amazon.fr")) {
    return "FRA";
  } else if (url.includes(".amazon.com")) {
    return "USA";
  }
  return "";
}

export function getRandomUserAgent() {
  const agents = [
    // Windows最新Chrome
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
    // macOS最新Safari
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15",
    // iPhone最新iOS
    "Mozilla/5.0 (iPhone14,6; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1",
    // 微信内置浏览器
    "Mozilla/5.0 (Linux; Android 14; M2012K11AG Build/TKQ1.221114.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 MMWEBID/9538 MicroMessenger/8.0.44.2500(0x28002C51) WeChat/arm64",
    // iPad Pro
    "Mozilla/5.0 (iPad; CPU OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1",
    // 华为鸿蒙手机
    "Mozilla/5.0 (Linux; HarmonyOS; LIO-AN00m; HMSCore 6.13.0.302) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36",
    // 桌面版Opera
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 OPR/107.0.0.0",
  ];
  return agents[Math.floor(Math.random() * agents.length)];
}

export function sleep(delay = 2000) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(true);
    }, delay);
  });
}

/**
 * 从链接中提取文件类型（后缀名）
 * @param {string} url - 待提取的链接
 * @returns {string|null} - 提取到的文件后缀（小写），无后缀时返回null
 */
export function getFileTypeFromUrl(url: string) {
  if (typeof url !== "string" || url.trim() === "") {
    return null;
  }
  // 处理可能包含查询参数或哈希的情况（如 ?v=123 或 #top）
  const cleanedUrl = url.split(/[?#]/)[0];

  // 查找最后一个 '.' 的位置
  const lastDotIndex = cleanedUrl.lastIndexOf(".");

  // 没有 '.' 或 '.' 是最后一个字符时，返回null
  if (lastDotIndex === -1 || lastDotIndex === cleanedUrl.length - 1) {
    return null;
  }

  // 提取后缀并转为小写（统一格式）
  const fileType = cleanedUrl.slice(lastDotIndex + 1).toLowerCase();

  // 过滤掉可能的特殊字符（确保只保留字母数字）
  return fileType.replace(/[^a-zA-Z0-9]/g, "");
}

export function removeUrlSearchParams(url: string) {
  // 查找问号的位置
  const questionMarkIndex = url.indexOf("?");
  // 如果存在问号，则返回问号之前的部分；否则返回原 URL
  return questionMarkIndex !== -1 ? url.substring(0, questionMarkIndex) : url;
}

/**
 * 格式化时间函数
 * @param {string} [format='YYYY-MM-DD HH:mm:ss'] - 时间格式模板
 * @param {Date | number | string} [date=new Date()] - 可选的时间对象/时间戳/日期字符串，默认当前时间
 * @returns {string} 格式化后的时间字符串
 */
export function formatTime(
  format: string = "YYYY-MM-DD HH:mm:ss",
  date: Date | number | string = new Date()
): string {
  // 处理时间参数，确保转为有效的 Date 对象
  const targetDate = date instanceof Date ? date : new Date(date);

  // 处理无效日期的情况
  if (isNaN(targetDate.getTime())) {
    throw new Error("Invalid date provided");
  }

  // 时间单位映射表
  const timeMap: Record<string, string | number> = {
    YYYY: targetDate.getFullYear(),
    MM: String(targetDate.getMonth() + 1).padStart(2, "0"),
    DD: String(targetDate.getDate()).padStart(2, "0"),
    HH: String(targetDate.getHours()).padStart(2, "0"),
    mm: String(targetDate.getMinutes()).padStart(2, "0"),
    ss: String(targetDate.getSeconds()).padStart(2, "0"),
    ms: String(targetDate.getMilliseconds()).padStart(3, "0"),
    M: targetDate.getMonth() + 1,
    D: targetDate.getDate(),
    H: targetDate.getHours(),
    m: targetDate.getMinutes(),
    s: targetDate.getSeconds(),
  };

  // 替换格式模板中的占位符
  return format.replace(
    /YYYY|MM|M|DD|D|HH|H|mm|m|ss|s|ms/g,
    (match) => timeMap[match]?.toString() || match
  );
}

export function customDeepClone(obj: any, cache = new WeakMap()) {
  // 原始类型直接返回
  if (obj === null || typeof obj !== "object") return obj;

  // 避免循环引用
  if (cache.has(obj)) return cache.get(obj);

  // 处理各种对象类型
  let clone: any;

  const Constructor = obj.constructor;

  // Date
  if (obj instanceof Date) {
    clone = new Date(obj.getTime());
    return clone;
  }

  // RegExp
  if (obj instanceof RegExp) {
    clone = new RegExp(obj.source, obj.flags);
    return clone;
  }

  // Map
  if (obj instanceof Map) {
    clone = new Map();
    cache.set(obj, clone);
    obj.forEach((value, key) => {
      clone.set(key, customDeepClone(value, cache));
    });
    return clone;
  }

  // Set
  if (obj instanceof Set) {
    clone = new Set();
    cache.set(obj, clone);
    obj.forEach((value) => {
      clone.add(customDeepClone(value, cache));
    });
    return clone;
  }

  // TypedArray
  if (ArrayBuffer.isView(obj)) {
    return new Constructor(obj); // 直接复制
  }

  // 普通对象或数组
  clone = Array.isArray(obj) ? [] : {};
  cache.set(obj, clone);

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = customDeepClone(obj[key], cache);
    }
  }

  return clone;
}
